﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Rally_like_a_madman.Data;
using Microsoft.Phone.Controls.Maps;
using System.Device.Location;
using Microsoft.Phone.Tasks;
using Rally_like_a_madman.BingMapsGeocodeService;
using System.Collections.ObjectModel;

namespace Rally_like_a_madman
{
    public partial class AddNewWaypointLocationPage : PhoneApplicationPage
    {
        public Waypoint activeWaypoint;

        private GeoCoordinateWatcher geoCoordinateWatcher;
        private MapLayer pushPinLayer;
        private MapLayer geocodePushPinLayer;
        private Pushpin currentLocationPushpin;
        private Pushpin tapLocationPushpin;
        private bool locationUnknown;

        public AddNewWaypointLocationPage()
        {
            InitializeComponent();

            setLocation();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tbSearch_GotFocus(object sender, RoutedEventArgs e)
        {
            if (tbSearch.Text == "Zoeken...")
                tbSearch.Text = string.Empty;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tbSearch_LostFocus(object sender, RoutedEventArgs e)
        {
            if (tbSearch.Text == string.Empty)
                tbSearch.Text = "Zoeken...";
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSearch_Click(object sender, RoutedEventArgs e)
        {
            if (tbSearch.Text != "Zoeken...")
                GeocodeServiceQuery();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCurrentLocation_Click(object sender, RoutedEventArgs e)
        {
            if (!locationUnknown)
            {
                //Center and zoom in on the current position
                map.Center = new GeoCoordinate(currentLocationPushpin.Location.Latitude, currentLocationPushpin.Location.Longitude);
                map.ZoomLevel = 16;
                //Clean all pushpins from the GeocodeService
                geocodePushPinLayer = new MapLayer();
            }
            else
                MessageBox.Show("Huidige locatie onbekend.");
        }

        /// <summary>
        /// 
        /// </summary>
        private void setLocation()
        {
            geoCoordinateWatcher = new GeoCoordinateWatcher(GeoPositionAccuracy.Default);
            geoCoordinateWatcher.PositionChanged += geoCoordinateWatcherPositionChanged;
            geoCoordinateWatcher.Start();

            var currentPosition = geoCoordinateWatcher.Position;

            double latitude = 51.5854;
            double longitude = 4.7933;

            currentLocationPushpin = new Pushpin();

            if (!currentPosition.Location.IsUnknown)
            {
                latitude = currentPosition.Location.Latitude;
                longitude = currentPosition.Location.Longitude;
                currentLocationPushpin.Content = "Huidige locatie";
            }
            else
            {
                locationUnknown = true;
                currentLocationPushpin.Content = "Locatie onbekend";
            }
                
            map.Center = new GeoCoordinate(latitude, longitude);

            pushPinLayer = new MapLayer();
            map.Children.Add(pushPinLayer);

            currentLocationPushpin.Location = new GeoCoordinate(latitude, longitude);
            
            pushPinLayer.Children.Add(currentLocationPushpin);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void geoCoordinateWatcherPositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
        {
            var currentLatitude = e.Position.Location.Latitude;
            var currentLongitude = e.Position.Location.Longitude;

            GeoCoordinate userLocation = new GeoCoordinate(e.Position.Location.Latitude, e.Position.Location.Longitude);
            currentLocationPushpin.Location = userLocation;
            currentLocationPushpin.Content = "Huidige locatie";

            if(locationUnknown)
            {
                map.Center = new GeoCoordinate(currentLocationPushpin.Location.Latitude, currentLocationPushpin.Location.Longitude);
                locationUnknown = false;
            }

            redrawMap();
        }

        /// <summary>
        /// 
        /// </summary>
        private void redrawMap()
        {
            pushPinLayer.Children.Clear();

            if(tapLocationPushpin != null)
            {
                pushPinLayer.Children.Add(tapLocationPushpin);
                map.Center = new GeoCoordinate(tapLocationPushpin.Location.Latitude, tapLocationPushpin.Location.Longitude);
            }

            pushPinLayer.AddChild(currentLocationPushpin, currentLocationPushpin.Location);

            if (geoCoordinateWatcher.Status == GeoPositionStatus.Ready)
                geoCoordinateWatcher.Start();
        }

        /// <summary>
        /// 
        /// </summary>
        private void GeocodeServiceQuery()
        {
            map.Children.Remove(geocodePushPinLayer);
            geocodePushPinLayer = new MapLayer();
            map.Children.Add(geocodePushPinLayer);

            var geocodeRequest = new GeocodeRequest
            {
                Options = new GeocodeOptions
                {
                    Filters = new ObservableCollection<FilterBase>
                    {
                        new ConfidenceFilter {MinimumConfidence = Confidence.Medium}
                    }
                }
            };

            var serviceClient = new GeocodeServiceClient("BasicHttpBinding_IGeocodeService");
            geocodeRequest.Query = tbSearch.Text;
            serviceClient.GeocodeCompleted += (o, args) =>
            {
                //if no error, display all the results in the map
                if (args.Error != null)
                    return;

                var response = args.Result;
                var results = from result in response.Results
                              select new Pushpin
                              {
                                  Content = result.DisplayName,
                                  Background = (Brush)Application.Current.Resources["PhoneAccentBrush"],
                                  Location = result.Locations[0]
                              };
                var coordinates = new LocationCollection();
                foreach (var pushpin in results)
                {
                    geocodePushPinLayer.Children.Add(pushpin);
                    coordinates.Add(pushpin.Location);
                }
                //If more than 1 result, show them all
                if (coordinates.Count > 1)
                {
                    map.SetView(LocationRect.CreateLocationRect(coordinates));
                }
                //If only 1 result, zoom in on it
                else if (coordinates.Count == 1)
                {
                    map.Center = coordinates[0];
                    map.ZoomLevel = 16;
                }
                //Else show an error
                else
                    MessageBox.Show("Geen resultaat gevonden.");
            };

            //Set map control's credentials to this request and begin to request
            map.CredentialsProvider.GetCredentials(credentials =>
            {
                geocodeRequest.Credentials = credentials;
                serviceClient.GeocodeAsync(geocodeRequest);
            });
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void map_Tap(object sender, GestureEventArgs e)
        {
            Point tapPosition = e.GetPosition(this.map);

            GeoCoordinate geo = new GeoCoordinate();
            geo = map.ViewportPointToLocation(tapPosition);

            map.Children.Remove(tapLocationPushpin);
            tapLocationPushpin = new Pushpin();
            tapLocationPushpin.Location = geo;

            //Set the text to the location
            tblLongitude.Text = "Lengtegraad: " + geo.Longitude;
            tblLatitude.Text = "Breedtegraad: " + geo.Latitude;

            redrawMap();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSetLocation_Click(object sender, RoutedEventArgs e)
        {
            if (tapLocationPushpin != null)
            {
                if (tapLocationPushpin.Location != null)
                {
                    //Set the location of the active Waypoint to the tapped location
                    activeWaypoint.GPSLongitude = tapLocationPushpin.Location.Longitude;
                    activeWaypoint.GPSLatitude = tapLocationPushpin.Location.Latitude;

                    NavigationService.GoBack();
                }
                else
                    MessageBox.Show("Geen geldige locatie. Druk op de map om een geldige locatie te kiezen.");
            }
            else
                MessageBox.Show("Druk ergens op de map om een geldige locatie te kiezen.");
        }
    }
}